#pragma once

class Player;
class Creature;

class QuestLog : public lua::binder, public WheelTimerOwner
{
public:
	QuestLog(Player* pOwner);
	~QuestLog();

	void Init(const QuestPrototype* pQuestProto);
	void Load(const QuestPrototype* pQuestProto, const inst_quest_prop& questProp);

	void PostInit();
	void Failed();

	Player* GetOwner() const { return m_pOwner; }
	uint32 GetQuestGuid() const { return m_questProp.questGuid; }
	uint32 GetQuestTypeID() const { return m_pQuestProto->questTypeID; }
	const QuestPrototype* GetQuestProto() const { return m_pQuestProto; }
	const inst_quest_prop& GetQuestProp() const { return m_questProp; }
	const QuestPrototype::Flags& GetQuestFlags() const { return m_pQuestProto->questFlags; }

	bool CanUpdateStatus(bool isRevertable = false) const;
	bool IsQuestFinished(bool isCheckAll = false) const;
	bool IsQuestFlagsFinished() const;
	bool IsQuestFlagsFailed() const;

	bool OnTalkNPC(Creature* pCreature);
	bool OnKillCreature(Creature* pCreature);
	bool OnHaveCheque(ChequeType chequeType, uint64 chequeValue, CHEQUE_FLOW_TYPE flowType, params<uint32> flowParams);
	bool OnHaveItem(uint32 itemTypeID, uint32 itemCount, ITEM_FLOW_TYPE flowType, params<uint32> flowParams);
	bool OnUseItem(uint32 itemTypeID, uint32 itemCount, uint32 actionUniqueKey);
	bool OnPlayStory(bool isSucc);

	bool SetQuestFinished();

	void RefreshQuestFinishStatus();

	void CleanResource4SubmitQuestAction();

	void QueryQuestNavData(int8 navIdx);
	void PushQuestNavData(int8 navIdx, const QuestNavData& navData, int flags = 0);

	void SendNewQuestInstance() const;
	void SendUpdateQuestStatus() const;

	uint32 GetQuestLackItemCount(uint32 itemTypeID) const;

	bool IsQuestCare(QuestConditionType type) const;
	void SetQuestSubmitting() { m_isQuestSubmitting = true; }

private:
	void RefreshQuestConditionStatus();

	void EventQuestTimeout();

	enum class OpQuestType {
		None, Add, Assign, Other,
	};
	bool TryTriggerEvent(bool isRevertable, QuestConditionType type,
		const std::function<std::pair<OpQuestType, int64>
		(size_t, const QuestCondition&, inst_quest_prop::Condition&)>& func);

	void PushQuestNavDataByGuide(int8 navIdx, const QuestNavObjInst& navObjInst);
	void PushQuestNavDataByNPC(int8 navIdx, const QuestNavObjInst& navObjInst);
	void PushQuestNavDataBySObj(int8 navIdx, const QuestNavObjInst& navObjInst);
	void PushQuestNavDataByNPCPt(int8 navIdx, const QuestNavObjInst& navObjInst);
	void PushQuestNavDataBySObjPt(int8 navIdx, const QuestNavObjInst& navObjInst);
	void QuestObjInst2QuestNavObjInst(
		const QuestObjInst& objInst, QuestNavObjInst& navObjInst) const;
	void QuestCondition2QuestNavObjInst(
		const QuestCondition& questCondition, QuestNavObjInst& navObjInst) const;
	void SendQuestNavData(int8 navIdx, const QuestNavData& navData) const;

	bool IsQuestConditionsReached(bool isCheckAll = false) const;

	virtual WheelTimerMgr *GetWheelTimerMgr();

	Player* const m_pOwner;
	const QuestPrototype* m_pQuestProto;
	QuestPrototype* m_pProtoPrivate;
	inst_quest_prop m_questProp;
	std::bitset<(size_t)QuestConditionType::Count> m_questCares;
	std::unordered_map<int8, QuestNavData> m_questNavDatas;

	bool m_isQuestSubmitting;
};

inline bool QuestLog::IsQuestFlagsFinished() const {
	return BIT_ISSET(m_questProp.questFlags, inst_quest_prop::IsFinished);
}
inline bool QuestLog::IsQuestFlagsFailed() const {
	return BIT_ISSET(m_questProp.questFlags, inst_quest_prop::IsFailed);
}
inline bool QuestLog::IsQuestCare(QuestConditionType type) const {
	return m_questCares.test((size_t)type);
}
